/*******************************************************************************
 * Copyright (C) 2010 Devin Samarin.
 * 
 * This file is part of the SUIT Toolkit.
 * 
 * The SUIT Toolkit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * The SUIT Toolkit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with the SUIT Toolkit.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package suit.ui;

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

/**
 * A slider control which allows the user to manipulate a numeric value. There
 * is a horizontal orient (Horiz) and vertical orient (Vert) for you to choose
 * from. The Scale widget contains a value which has an associated lower and
 * upper bound, together with step increments.
 * 
 * @author Devin Samarin
 * 
 */
public class Scale extends Widget {
	
	protected int focusable = FOCUS_TRUE;

	private int orient = 1;

	/**
	 * Constant used to orient the Scale horizontally (left to right)
	 */
	public static final int Horiz = 1;
	/**
	 * Constant used to orient the Scale vertically (bottom to top)
	 */
	public static final int Vert = 2;

	private double value;

	private double lower;
	private double upper;
	private double step;

	private Font font = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN,
			Font.SIZE_SMALL);

	/**
	 * Constructs a new Scale widget
	 * 
	 * @param orient
	 *            used to orient the Scale vertically or horizontally; use the
	 *            Horiz or Vert constants
	 * @param lower
	 *            the lowest amount that the slider can reach
	 * @param upper
	 *            the highest amount that the slider can reach
	 * @param step
	 *            the step increments that the slider must fall on
	 * @param value
	 *            the initial value of the slider
	 */
	public Scale(int orient, double lower, double upper, double step,
			double value) {
		this.lower = lower;
		this.upper = upper;
		this.step = step;
		this.value = value;
	}

	/**
	 * Sets the orient used to orient the Scale vertically or horizontally; use
	 * the Horiz or Vert constants
	 * 
	 * @param orient
	 */
	public void setOrient(int orient) {
		this.orient = orient;
		invalidate(this);
	}

	/**
	 * Sets the lowest amount that the slider can reach
	 * 
	 * @param lower
	 */
	public void setLower(double lower) {
		this.lower = lower;
		invalidate(this);
	}

	/**
	 * Sets the highest amount that the slider can reach
	 * 
	 * @param upper
	 */
	public void setUpper(double upper) {
		this.upper = upper;
		invalidate(this);
	}

	/**
	 * Sets the step increments that the slider must fall on
	 * 
	 * @param step
	 */
	public void setStep(double step) {
		this.step = step;
		invalidate(this);
	}

	/**
	 * Set the value of the slider
	 * 
	 * @param value
	 */
	public void setValue(double value) {
		this.value = value;
		invalidate(this);
	}

	/**
	 * Get the orientation of the scale
	 * 
	 * @return Horiz or Vert
	 */
	public int getOrient() {
		return orient;
	}

	/**
	 * Get the lowest amount that the slider can reach
	 * 
	 * @return the lowest amount that the slider can reach
	 */
	public double getLower() {
		return lower;
	}

	/**
	 * Get the highest amount that the slider can reach
	 * 
	 * @return the highest amount that the slider can reach
	 */
	public double getUpper() {
		return upper;
	}

	/**
	 * Get the step increment that the slider must fall on
	 * 
	 * @return the step increment that the slider must fall on
	 */
	public double getStep() {
		return step;
	}

	/**
	 * Get the value of the slider
	 * 
	 * @return the value of the slider
	 */
	public double getValue() {
		return value;
	}

	protected Rectangle getRequisition() {
		return new Rectangle(0, 0, orient == Horiz ? 100 : 20,
				orient == Horiz ? 20 : 100);
	}

	protected boolean onExpose(Event e) {
		e.graphics.setColor(0xFFFFFF);
		e.graphics.setFont(font);
		int pos;
		if (orient == Horiz) {
			pos = (int) (allocation.x + (value - lower) / (upper - lower)
					* allocation.w);
			e.graphics.drawLine(allocation.x, allocation.y + allocation.h / 2,
					allocation.x + allocation.w, allocation.y + allocation.h
							/ 2);
			e.graphics.drawLine(pos, allocation.y + allocation.h / 2 - 10, pos,
					allocation.y + allocation.h / 2 + 10);
			e.graphics.drawString(Double.toString(value), pos, allocation.y
					+ allocation.h / 2, Graphics.HCENTER | Graphics.BOTTOM);
		} else if (orient == Vert) {
			pos = (int) (allocation.y + (value - lower) / (upper - lower)
					* allocation.h);
			e.graphics.drawLine(allocation.x - 10, pos, allocation.x + 10, pos);
			e.graphics.drawLine(allocation.x + allocation.w / 2 - 10, pos,
					allocation.x + allocation.w / 2 + 10, pos);
			e.graphics
					.drawString(Double.toString(value), allocation.x
							+ allocation.w / 2, pos, Graphics.RIGHT
							| Graphics.BASELINE);
		}
		return false;
	}

	protected boolean onPointerPressed(Event e) {
		return onPointerDragged(e);
	}

	protected boolean onPointerDragged(Event e) {
		Event l;
		if ((l = localizeEvent(e)) != null) {
			if (orient == Horiz) {
				value = Math.floor((lower
						+ ((double) l.x / (double) allocation.w)
						* (upper - lower) + step / 2)
						/ step)
						* step;
			} else {
				value = Math.floor((lower
						+ ((double) l.y / (double) allocation.h)
						* (upper - lower) + step / 2)
						/ step)
						* step;
			}
			if (value > upper)
				value = upper;
			else if (value < lower)
				value = lower;
			invalidate(this);
		}
		return false;

	}

}
